#include "../include/server.h"
int deal_cmd(Task *task)
{
    Train train;
    int ret;
    while (1)
    {
        memset(&train, 0, sizeof(Train));
        ret = recvn(task->usr.sfd, &train.len, 4);
        ret = recvn(task->usr.sfd, train.buf, train.len);
        time_t t1;
        syslog(LOG_INFO, "LOG INFO:thread receive usr_id:%d cmd:%s time:%s", task->usr.usr_id, train.buf, ctime(&t1));
        printf("----\n");
        printf("deal_cmd=%s\n", train.buf);
        if (strncmp(train.buf, "cd", 2) == 0)
        {
            do_cd(task, train.buf + 3);
        }
        else if (strncmp(train.buf, "ls", 2) == 0)
        {
            do_ls(task);
        }
        else if (strncmp(train.buf, "pwd", 3) == 0)
        {
            do_pwd(task);
        }
        else if (strncmp(train.buf, "puts", 4) == 0)
        {
            do_puts(task, train.buf + 5);
        }
        else if (strncmp(train.buf, "gets", 4) == 0)
        {
            do_gets(task, train.buf + 5);
        }
        else if (strncmp(train.buf, "rm", 2) == 0)
        {
            do_rm(task, train.buf + 3);
        }
        else if (strncmp(train.buf, "mkdir", 5) == 0)
        {
            do_mkdir(task, train.buf + 6);
        }
        else if (strncmp(train.buf, "exit", 4) == 0)
        {
            return -1;
        }
    }
    return 0;
}
void do_cd(Task *task, char *dirname)
{
    // cd后发送一个标志表示是否成功进入，用0表示失败，1表示成功
    Train train;
    memset(&train, 0, sizeof(train));
    MYSQL_RES *mysql_res;
    MYSQL_ROW mysql_row; //行数据
    int ret_flag = F_ret;
    if (strcmp(dirname, ".") == 0)
    {
        ret_flag = S_ret;
    }
    else if (strcmp(dirname, "..") == 0)
    {
        char sql_cmd[MAXSTR] = {0};
        sprintf(sql_cmd, "select type,parent_id from TFILE where owner_id=%d and id=%d;", task->usr.usr_id, task->usr.dir_id);
        int ret = mysql_real_query(task->mysql_con, sql_cmd, strlen(sql_cmd));
        if (ret != 0)
        {
            printf("select error:%s\n", mysql_error(task->mysql_con));
        }
        MYSQL_RES *mysql_res;
        MYSQL_ROW mysql_row; //行数据
        mysql_res = mysql_store_result(task->mysql_con);
        while ((mysql_row = mysql_fetch_row(mysql_res)) != NULL)
        {
            if (0 == atoi(mysql_row[1]))
            {
                printf("this is root dir\n");
                ret_flag = F_ret;
                break;
            }
            else if (1 == atoi(mysql_row[0]))
            {
                task->usr.dir_id = atoi(mysql_row[1]);
                ret_flag = S_ret;
                break;
            }
        }
    }
    else
    {
        mysql_res = select_ls_cd_sql(task);
        while ((mysql_row = mysql_fetch_row(mysql_res)) != NULL)
        {
            if (strcmp(mysql_row[0], dirname) == 0 && 1 == atoi(mysql_row[1]))
            {
                task->usr.dir_id = atoi(mysql_row[2]);
                ret_flag = S_ret;
                break;
            }
        }
    }
    train.len = ret_flag;
    sendn(task->usr.sfd, &train, 4);
}
void do_ls(Task *task)
{
    //发送文件名，最后发送train.len=0表示传输完成
    Train train;
    memset(&train, 0, sizeof(train));
    MYSQL_RES *mysql_res;
    MYSQL_ROW mysql_row; //行数据
    mysql_res = select_ls_cd_sql(task);
    while ((mysql_row = mysql_fetch_row(mysql_res)) != NULL)
    {
        memset(&train, 0, sizeof(Train));
        if (0 == atoi(mysql_row[1]))
        {
            sprintf(train.buf, "- %s", mysql_row[0]);
        }
        else
        {
            sprintf(train.buf, "d %s", mysql_row[0]);
        }
        train.len = strlen(train.buf);
        printf("train=%s\n", train.buf);
        sendn(task->usr.sfd, &train, 4 + train.len);
    }
    memset(&train, 0, sizeof(train));
    train.len = 0;
    sendn(task->usr.sfd, &train, 4);
    printf("ls finish\n");
}
void do_pwd(Task *task)
{
    //小火车直接发送当前目录
    char pwd_str[MAXSTR] = {0};
    char **res = malloc(sizeof(char *) * DIR_LEVEL);
    int count = 0;
    MYSQL_RES *mysql_res;
    MYSQL_ROW mysql_row; //行数据
    int cur_dir_id = task->usr.dir_id;
    while (cur_dir_id != 0)
    {
        mysql_res = do_pwd_sql(task->mysql_con, cur_dir_id);
        mysql_row = mysql_fetch_row(mysql_res);
        res[count] = calloc(1, sizeof(char) * (strlen(mysql_row[0]) + 1));
        strcpy(res[count], mysql_row[0]);
        count++;
        cur_dir_id = atoi(mysql_row[1]);
    }
    int str_len = 0;
    for (int i = count - 1; i >= 0; i--)
    {
        strcpy(pwd_str + str_len, "/");
        str_len++;
        strcpy(pwd_str + str_len, res[i]);
        str_len += strlen(res[i]);
        free(res[i]);
    }
    Train train;
    memset(&train, 0, sizeof(train));
    train.len = strlen(pwd_str);
    strcpy(train.buf, pwd_str);
    sendn(task->usr.sfd, &train, 4 + train.len);
    free(res);
}
void do_puts(Task *task, char *filename)
{
    int ret_flag, exist;
    char md5_str[33] = {0}, recv_md5[33] = {0}, real_md5[33] = {0};
    printf("do_puts\n");
    Train train;
    memset(&train, 0, sizeof(train));
    //接收标志，若文件不在客户端存在，无法上传
    recvn(task->usr.sfd, &ret_flag, 4);
    if (ret_flag == -1)
    {
        return;
    }

    //接收md5码
    recvn(task->usr.sfd, &train.len, 4);
    recvn(task->usr.sfd, recv_md5, train.len);
    printf("recv_md5=%s\n", recv_md5);

    int cur_size = selectfile_through_md5_sql(task->mysql_con, recv_md5);
    memset(&train, 0, sizeof(train));
    int real_size = 0;
    char last_name[MAXSTR] = "../user/";
    char new_name[MAXSTR] = "../user/";
    if (cur_size == -1) //磁盘上不存在该文件，接收文件。查找客户端下是否有该文件。
    {
        printf("md5 not exist in disk\n");
        memset(&train, 0, sizeof(train));
        train.len = -1;
        sendn(task->usr.sfd, &train, 4);                             //磁盘上不存在此文件
        cur_size = select_file_sql(task, filename, &exist, md5_str); //查看用户是否有该文件
        if (exist == 1)                                              //用户有原文件，发送MD5和文件大小，判断断点续传
        {
            printf("usr has filename\n");
            train.len = 0;
            sendn(task->usr.sfd, &train, 4); //标志，表示文件存在

            memset(&train, 0, sizeof(train));
            train.len = strlen(md5_str); //发送MD5
            strcpy(train.buf, md5_str);
            sendn(task->usr.sfd, &train, 4 + train.len);

            memset(&train, 0, sizeof(train)); //发送文件大小
            train.len = sizeof(cur_size);
            memcpy(train.buf, &cur_size, train.len);
            sendn(task->usr.sfd, &train, 4 + train.len);

            int md5_equal = 0; //接收标志，表示MD5是否相等
            recvn(task->usr.sfd, &md5_equal, 4);

            //若MD5相等，则直接断点续传，若不相等，则重传
            if (md5_equal == 1) //断点续传。若该文件usenum等于1，直接修改磁盘文件名，修改md5表上的MD5，修改文件表上的MD5
            {                   //若该文件的usenum大于1，复制文件，新增md5表上的表项，原文件MD5表项减1，修改数据库上的MD5
                printf("md5_equal=1\n");
                int ret = select_md5_num(task, md5_str);
                if (ret == 1)
                {
                    printf("usenum=1\n");
                    real_size = recv_file(task->usr.sfd, md5_str);
                    real_size = real_size + cur_size;
                    strcat(last_name, md5_str);
                    compute_file_md5(last_name, real_md5, real_size);
                    strcat(new_name, real_md5);
                    rename(last_name, new_name);
                    //修改tmd5表项中的MD5
                    modify_tmd5_md5(task->mysql_con, md5_str, real_md5);
                }
                else
                {
                    //复制文件，进行文件续传，tmd5表中旧MD5表项减1，新增一项。
                    printf("usenum>1\n");
                    copy_file(md5_str, recv_md5);
                    printf("copy_file success\n");
                    real_size = recv_file(task->usr.sfd, recv_md5);
                    strcat(last_name, recv_md5);
                    real_size = real_size + cur_size;
                    compute_file_md5(last_name, real_md5, real_size);
                    //在旧md5表项中减1，新增一项
                    sub_md5_sql(task->mysql_con, md5_str);
                    insert_md5_sql(task, real_md5);
                }
                modify_file_info_sql(task, filename, real_md5, real_size);
            }
            else //不相等，则重传。若该文件usenum等于1，删除磁盘文件，修改MD5表上的表项，修改数据库上的MD5。
            {    //若该文件的usenum大于1，重创文件，新增MD5表项，修改文件表上的MD5，原MD5表项usenum减1
                printf("md5_equal!=1\n");
                int ret = select_md5_num(task, md5_str);
                if (ret == 1)
                {
                    printf("select_md5_num==1\n");
                    char tmp_path[MAXSTR] = "../user/";
                    strcat(tmp_path, md5_str);
                    remove(tmp_path);
                    //删除md5表中的这项
                    del_md5_sql(task->mysql_con, md5_str);
                }
                else //测试成功
                {
                    printf("select_md5_num>1\n");
                    // md5表中原表项-1
                    sub_md5_sql(task->mysql_con, md5_str);
                }
                real_size = recv_file(task->usr.sfd, recv_md5);
                strcat(last_name, recv_md5);
                compute_file_md5(last_name, real_md5, real_size);
                if (strcmp(real_md5, recv_md5) != 0) //文件只传了一半，修改名字
                {
                    strcat(new_name, real_md5);
                    rename(last_name, new_name);
                }
                //在MD5表项中添加一项
                insert_md5_sql(task, real_md5);
                modify_file_info_sql(task, filename, real_md5, real_size);
            }
        }
        else //用户无原文件，直接接收文件(测试成功)
        {
            printf("user does not have this filename\n");
            train.len = -1;
            sendn(task->usr.sfd, &train, 4); //发送标志，表示文件不存在，接收文件
            real_size = recv_file(task->usr.sfd, filename);
            char real_md5[33] = {0};
            strcat(last_name, filename);
            if (compute_file_md5(last_name, real_md5, real_size) == -1)
            {
                printf("compute_file_md5 error\n");
            }
            if (strcmp(real_md5, recv_md5) != 0)
            {
                strcpy(recv_md5, real_md5);
            }
            strcat(new_name, recv_md5);
            rename(last_name, new_name);
            printf("real_md5=%s\n", recv_md5);
            insert_md5_sql(task, recv_md5);
            Fat_File fat_file;
            strcpy(fat_file.filename, filename);
            fat_file.parent_id = task->usr.dir_id;
            fat_file.owner_id = task->usr.usr_id;
            strcpy(fat_file.md5, recv_md5);
            fat_file.filesize = real_size;
            fat_file.type = 0;
            insert_file_sql(task->mysql_con, &fat_file);
        }
    }
    else //文件存在，不用接收文件。
    {
        printf("md5 exist in disk\n");
        train.len = 0;
        sendn(task->usr.sfd, &train, 4); //标志，表示文件存在
        modify_md5_sql(task, recv_md5);
        real_size = cur_size;

        //若该用户在该目录下存在该文件名，则直接修改文件表中的大小和MD5，否则，插入新一行
        int ret = select_file_sql(task, filename, &exist, md5_str);
        if (exist == 1)
        {
            //查看原文件的使用数目，若为1，则直接删除，若大于1，则减1。修改数据库中文件表的大小和MD5
            modify_file_info_sql(task, filename, recv_md5, real_size);
            ret = select_md5_num(task, md5_str);
            if (ret == 1)
            {
                del_md5_sql(task->mysql_con, md5_str);
            }
            else if (ret > 1)
            {
                sub_md5_sql(task->mysql_con, md5_str);
            }
        }
        else
        {
            Fat_File fat_file;
            strcpy(fat_file.filename, filename);
            fat_file.parent_id = task->usr.dir_id;
            fat_file.owner_id = task->usr.usr_id;
            strcpy(fat_file.md5, recv_md5);
            fat_file.filesize = real_size;
            fat_file.type = 0;
            insert_file_sql(task->mysql_con, &fat_file);
        }
    }
}
void do_gets(Task *task, char *filename)
{
    int exist, ret, filesize;
    Train train;
    memset(&train, 0, sizeof(train));
    recvn(task->usr.sfd, &exist, 4);
    char file_md5[33] = {0};
    if (exist == 0) //客户端存在此文件，接收md5和filesize
    {
        printf("client has this file\n");
        memset(&train, 0, sizeof(train));
        recvn(task->usr.sfd, &train.len, 4);
        recvn(task->usr.sfd, &filesize, train.len);

        memset(&train, 0, sizeof(train));
        recvn(task->usr.sfd, &train.len, 4);
        recvn(task->usr.sfd, file_md5, train.len);

        char real_name[MAXSTR] = {0}, real_md5[33] = {0};
        select_md5_file(task, filename, real_name);
        char real_path[MAXSTR] = "../user/";
        strcat(real_path, real_name);
        compute_file_md5(real_path, real_md5, filesize);
        if (strcmp(real_md5, file_md5) == 0)
        { //断点续传
            memset(&train, 0, sizeof(train));
            train.len = 1;
            sendn(task->usr.sfd, &train, 4);
            send_file(task->usr.sfd, real_name, filesize);
        }
        else
        { //重新传
            memset(&train, 0, sizeof(train));
            train.len = -1;
            sendn(task->usr.sfd, &train, 4);
            send_file(task->usr.sfd, real_name, 0);
        }
    }
    else //客户端不存在此文件，直接发送.根据文件名查找md5码
    {
        printf("client does not have this file\n");
        ret = select_md5_file(task, filename, file_md5);
        if (ret == S_ret)
        {
            //发送文件打开成功标志,发送文件
            sendn(task->usr.sfd, &ret, 4);
            send_file(task->usr.sfd, file_md5, 0);
            printf("send_file success\n");
        }
        else
        {
            //发送文件打开失败标志
            sendn(task->usr.sfd, &ret, 4);
            return;
        }
    }
}
void do_rm(Task *task, char *filename)
{
    //从文件表中获取该文件的MD5，并在文件表中删除该文件，去md5表中查看文件的使用数目，若为1，则删除该项并且在磁盘上删除文件，否则，该表项减1.
    int ret = 0;
    Train train;
    memset(&train, 0, sizeof(train));

    char file_md5[33] = {0};
    if (select_md5_file(task, filename, file_md5) == S_ret)
    {
        del_file(task, filename);
        int usenum = select_md5_num(task, file_md5);
        if (usenum == 1)
        {
            char file_path[MAXSTR] = "../user/";
            strcat(file_path, file_md5);
            remove(file_path);
            del_md5_sql(task->mysql_con, file_md5);
        }
        else if (usenum > 1)
        {
            sub_md5_sql(task->mysql_con, file_md5);
        }
    }
    else
    {
        ret = -1;
    }
    if (ret == 0) //成功删除
    {
        train.len = 1;
    }
    else
    {
        train.len = 0;
    }
    sendn(task->usr.sfd, &train, 4);
}
void do_mkdir(Task *task, char *args)
{
    //需要发送0或1表示是否成功，0表示失败，1表示成功
    Train train;
    char sql_cmd[MAXSTR] = {0};
    sprintf(sql_cmd, "insert into TFILE(parent_id,filename,owner_id) values(%d,'%s',%d)", task->usr.dir_id, args, task->usr.usr_id);
    int ret = mysql_real_query(task->mysql_con, sql_cmd, strlen(sql_cmd));
    if (ret != 0)
    {
        ret = 0;
        printf("insert error:%s\n", mysql_error(task->mysql_con));
    }
    ret = 1;
    sendn(task->usr.sfd, &ret, 4 + train.len);
    printf("mkdir success\n");
}
int compute_file_md5(char *file_path, char *md5_str, int filesize)
{
    int i;
    int fd;
    int ret;
    unsigned char data[READ_DATA_SIZE];
    unsigned char md5_value[MD5_SIZE];
    MD5_CTX md5;
    fd = open(file_path, O_RDONLY);
    printf("strlen=%ld file_path=%s\n", strlen(file_path), file_path);
    if (-1 == fd)
    {
        perror("open");
        return -1;
    }
    // init md5
    MD5Init(&md5);
    int cur_size = 0, left_size = filesize, read_size;
    while (left_size > 0)
    {
        if (left_size < READ_DATA_SIZE)
        {
            read_size = left_size;
        }
        else
        {
            read_size = READ_DATA_SIZE;
        }
        ret = read(fd, data, read_size);
        if (-1 == ret)
        {
            perror("read");
            return -1;
        }
        left_size -= ret;
        cur_size += ret;
        MD5Update(&md5, data, ret);

        if (0 == ret || ret < READ_DATA_SIZE)
        {
            break;
        }
    }
    close(fd);
    MD5Final(&md5, md5_value);
    for (i = 0; i < MD5_SIZE; i++)
    {
        snprintf(md5_str + i * 2, 2 + 1, "%02x", md5_value[i]);
    }
    md5_str[MD5_STR_LEN] = '\0'; // add end
    return 0;
}
void copy_file(char *old, char *new)
{
    char old_path[MAXSTR] = "../user/";
    char new_path[MAXSTR] = "../user/";
    strcat(old_path, old);
    strcat(new_path, new);
    int fd1 = open(old_path, O_RDONLY);
    if (fd1 == -1)
    {
        printf("open error\n");
    }
    int fd2 = open(new_path, O_RDWR | O_CREAT | O_APPEND, 0666);
    if (fd2 == -1)
    {
        printf("open error\n");
    }
    while (1)
    {
        char buf[1024] = {0};
        int ret = read(fd1, buf, sizeof(buf));
        if (ret == 0)
        {
            break;
        }
        ret = write(fd2, buf, ret);
    }
    close(fd1);
    close(fd2);
}